﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Web.Hosting;
using System.Web.Security;
using WebShop.Contracts;
using WebShop.DAL.Repositories;
using WebShop.Models;

namespace WebShop.Infrastructure.Providers
{
    public class WebShopMembershipProvider : MembershipProvider
    {
        private string _applicationName;
        private bool _enablePasswordReset;
        private bool _enablePasswordRetrieval;
        private int _maxInvalidPasswordAttempts;
        private int _minRequiredNonalphanumericCharacters;
        private int _minRequiredPasswordLength;
        private int _passwordAttemptWindow;
        private MembershipPasswordFormat _passwordFormat = MembershipPasswordFormat.Hashed;
        private string _passwordStrengthRegularExpression;
        private bool _requiresQuestionAndAnswer;
        private bool _requiresUniqueEmail = true;

        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        #region override

        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            if (string.IsNullOrEmpty(username))
            {
                throw CreateArgumentNullOrEmptyException("userName");
            }
            if (string.IsNullOrEmpty(oldPassword))
            {
                throw CreateArgumentNullOrEmptyException("oldPassword");
            }
            if (string.IsNullOrEmpty(newPassword))
            {
                throw CreateArgumentNullOrEmptyException("newPassword");
            }
            using (var context = new NWDB())
            {
                User user = context.Users.FirstOrDefault(usr => usr.UserName == username);
                if (user == null)
                {
                    return false;
                }
                dynamic hashedPassword = user.Password;
                bool verificationSucceeded = (hashedPassword != null && user.Password.Equals(CreatePasswordHash(oldPassword, user.PasswordSalt)));
                if (verificationSucceeded)
                {
                    //user.PasswordFailuresSinceLastSuccess = 0;
                }
                else
                {
                    /*int failures = user.PasswordFailuresSinceLastSuccess;
                    if (failures != -1)
                    {
                        user.PasswordFailuresSinceLastSuccess += 1;
                        user.LastPasswordFailureDate = DateTime.UtcNow;
                    }*/
                    //context.SaveChanges();
                    return false;
                }
                String newsalt = CreateSalt();
                String newhashedPassword = CreatePasswordHash(newPassword, newsalt);
                if (newhashedPassword.Length > 128)
                {
                    throw new ArgumentException("Password too long");
                }
                user.Password = newhashedPassword;
                user.PasswordSalt = newsalt;
                //user.PasswordChangedDate = DateTime.UtcNow;
                context.SaveChanges();

                return true;
            }
        }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new NotSupportedException();
        }

        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            var args = new ValidatePasswordEventArgs(username, password, true);

            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            MembershipUser u = GetUser(username, false);

            if (u == null)
            {
                using (var context = new NWDB())
                {
                    String passwordSalt = GenerateKey();
                    var user = new User
                                   {
                                       UserName = username,
                                       Email = email,
                                       PasswordSalt = passwordSalt,
                                       IsLockedOut = false,
                                       CreatedDate = DateTime.Now,
                                       LastLockedOutDate = DateTime.Now,
                                       LastLoginDate = DateTime.Now,
                                       Password = CreatePasswordHash(password, passwordSalt)
                                   };

                    if (!isApproved)
                    {
                        user.IsActivated = false;
                        user.NewEmailKey = Guid.NewGuid().ToString();
                    }
                    else
                    {
                        user.IsActivated = true;
                    }

                    // Change if you want to use registration through email
                    if (!isApproved)
                    {
                        string activationLink = "http://localhost:PORT/Account/Activate/" + user.UserName + "/" + user.NewEmailKey;

                        var message = new MailMessage("EMAIL", user.Email)
                                          {
                                              Subject = "Activate your account",
                                              Body = activationLink
                                          };

                        var client = new SmtpClient("SERVER");
                        client.Credentials = new NetworkCredential("LOGIN", "PASSWORD");
                        client.EnableSsl = true;
                        client.Send(message);
                    }

                    context.Users.Add(user);
                    context.SaveChanges();

                    status = MembershipCreateStatus.Success;

                    return GetUser(username, false);
                }
            }
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }

            return null;
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new NotImplementedException();
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            var coll = new MembershipUserCollection();
            using (var context = new NWDB())
            {
                List<User> users = context.Users.OrderBy(u => u.UserName).Skip(pageIndex * pageSize).Take(pageSize).ToList();

                foreach (User user in users)
                {
                    MembershipUser membershipUser = ExtractUser(user);
                    coll.Add(membershipUser);
                }
            }

            totalRecords = coll.Count;

            return coll;
        }

        public override int GetNumberOfUsersOnline()
        {
            throw new NotImplementedException();
        }

        public override string GetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            using (var context = new NWDB())
            {
                User user = context.Users.SingleOrDefault(u => u.UserName == username);

                return ExtractUser(user);
            }
        }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            throw new NotImplementedException();
        }

        public override string GetUserNameByEmail(string email)
        {
            using (var context = new NWDB())
            {
                User user = context.Users.SingleOrDefault(u => u.Email == email);

                if (user == null)
                {
                    return "";
                }

                return user.UserName;
            }
        }

        public override string ResetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }

        public override bool UnlockUser(string userName)
        {
            throw new NotImplementedException();
        }

        public override void UpdateUser(MembershipUser user)
        {
            throw new NotImplementedException();
        }

        public override bool ValidateUser(string username, string password)
        {
            using (var context = new NWDB())
            {
                User user = context.Users.SingleOrDefault(u => u.UserName == username);

                if (user == null)
                {
                    return false;
                }

                if (user.Password.Equals(CreatePasswordHash(password, user.PasswordSalt)))
                {
                    return true;
                }

                return false;
            }
        }

        #region Properties

        public override bool EnablePasswordReset
        {
            get { return _enablePasswordReset; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return _enablePasswordRetrieval; }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return _maxInvalidPasswordAttempts; }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return _minRequiredNonalphanumericCharacters; }
        }

        public override int MinRequiredPasswordLength
        {
            get { return _minRequiredPasswordLength; }
        }

        public override int PasswordAttemptWindow
        {
            get { return _passwordAttemptWindow; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { return _passwordFormat; }
        }

        public override string PasswordStrengthRegularExpression
        {
            get { return _passwordStrengthRegularExpression; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return _requiresQuestionAndAnswer; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return _requiresUniqueEmail; }
        }

        #endregion

        #endregion

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            if (name.Length == 0)
                name = "MyMembershipProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "NW Membership Provider");
            }

            base.Initialize(name, config);

            _applicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
            _maxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            _passwordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
            _minRequiredNonalphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonalphanumericCharacters"], "1"));
            _minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "6"));
            _enablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
            _passwordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
        }

        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (string.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

        private ArgumentException CreateArgumentNullOrEmptyException(string paramName)
        {
            return new ArgumentException(string.Format("Argument cannot be null or empty: {0}", paramName));
        }

        private static MembershipUser ExtractUser(User user)
        {
            if (user == null)
            {
                return null;
            }
            var membershipUser = new MembershipUser("MyMembershipProvider", user.UserName, user.UserId, user.Email, "", user.Comments, user.IsActivated, user.IsLockedOut, user.CreatedDate, user.LastLoginDate, DateTime.Now,
                                            DateTime.Now, user.LastLockedOutDate);

            return membershipUser;
        }

        #region pasword

        private static string CreateSalt()
        {
            var rng = new RNGCryptoServiceProvider();
            var buff = new byte[32];
            rng.GetBytes(buff);

            return Convert.ToBase64String(buff);
        }

        private static string CreatePasswordHash(string pwd, string salt)
        {
            string saltAndPwd = String.Concat(pwd, salt);
            string hashedPwd = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPwd, "sha1");

            return hashedPwd;
        }

        private static string GenerateKey()
        {
            Guid emailKey = Guid.NewGuid();

            return emailKey.ToString();
        }

        #endregion
    }
}